home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PCMania 39
/
PCMania CD39_2.iso
/
pcmania
/
treal39
/
frgen.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1993-09-29
|
20KB
|
795 lines
/*-------------------------------------------------------------------------
Fractal Landscape Generator v1.4
Copyright (c) 1993 Steve Anger
This program is freely distributable
FRGEN is a utility to generate fractal landscapes and shapes using
successive triangle sub-division. The fractal data can be output as
POV-Ray 1.0/2.0 or Vivid scene description files or as raw triangle
data.
Notes for compiling: This program has been successfully compiled with
Borland C++ 3.1 and DJGCC C/C++ 2.4.1. Under Turbo/Borland C++ compile
with COMPACT or LARGE memory model with "Copy propagation" optimization
disabled.
CompuServe: 70714,3113
YCCMR BBS: (708)358-5611
Modified for Mac MPW C++ support 4/16/93 - Eduard [esp] Schwan
-------------------------------------------------------------------------*/
#include <fstream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <values.h>
#include <time.h>
#include "vect.h"
#ifdef __TURBOC__
#include <graphics.h>
#include <conio.h>
#include <dos.h>
extern unsigned _stklen = 16000; // Larger stack size for recursion
#endif
#ifdef applec
#include <CursorCtl.h> // for multitasking
#endif
typedef enum bool {false = 0, true = 1};
typedef enum Format {POV10, POV20, VIVID, RAW, NONE};
const char ver[] = "v1.4";
// Function prototypes
void usage (void);
void abortmsg (char *msg, int exit_code);
void add_ext (char *fname, char *ext, bool force);
void swap (float &a, float &b);
void process_args (int argc, char *argv[]);
void file_args (void);
void process_option (char *s);
void write_options (void);
bool read_triangle (Vector&a, Vector&b, Vector&c, int &fix_ab, int &fix_bc, int
&fix_ca);
Vector noise_vector (Vector&v, int level);
void tri_fractal (Vector&a, Vector&b, Vector&c, int fix_ab, int fix_bc, int
fix_ca, int level, Vector&vmin, Vector&vmax);
void write_light (fstream &f, Vector pos);
void write_camera (fstream &f, Vector pos, Vector target);
void set_view (void);
char upcase (char c);
void plot_tri (const Vector&a, const Vector&b, const Vector&c);
int init_display (void);
void close_display (void);
void check_abort (void);
// Global variables
fstream in, out, data;
Vector viewpoint; // Where you're looking from
Vector lookat; // Where you're looking at
Vector nx, ny, nz; // Basis vectors for viewpoint
Vector nscale; // Noise scaling factors
Vector nbias; // Noise biasing factors
Vector nfalloff; // Noise falloff factor
int depth; // Depth of recursion
int seed; // Seed for random number generator
bool display; // Set to true if display preview enabled
bool swap_yz; // Swap y and z coords on output
Format format; // Output file format
char infile[64]; // Input file name
char mainfile[64]; // Output file name
char datafile[64]; // Include file name
long tri_count; // Count of triangles generated
int input_line; // Current input line being parsed
Vector gmin, gmax;
int main (int argc, char* argv[])
{
cerr << "\nFractal Landscape Generator " << ver << " ";
cerr << "Copyright (c) 1993 Steve Anger\n";
cerr << "This program is freely distributable\n\n";
srand ((unsigned)time(NULL));
seed = (int)(10000.0 * rand() / (float)RAND_MAX);
infile[0] = '\0';
mainfile[0] = '\0';
viewpoint = Vector (10.0, 10.0, -10.0);
lookat = Vector (0.0, 0.0, 0.0);
nscale = Vector (0.10, 0.10, 0.10);
nbias = Vector (0.0, 0.0, 0.0);
nfalloff = Vector (1.0, 1.0, 1.0);
depth = 3;
display = false;
swap_yz = false;
format = POV20;
tri_count = 0;
input_line = 0;
for (int i = 1; i < argc; i++) {
if (argv[i][0] != '-' && argv[i][0] != '/') {
if (strlen(infile) == 0) {
strcpy (infile, argv[i]);
add_ext (infile, "fr", false);
}
else if (strlen(mainfile) == 0)
strcpy (mainfile, argv[i]);
else
abortmsg ("Too many file names specified.", 1);
}
}
if (strlen (infile) == 0) {
usage();
exit (1);
}
in.open (infile, ios::in);
if (!in) abortmsg ("Error opening input file.", 1);
file_args(); // Get options from input file
process_args (argc, argv); // Get the command line options
if (strlen (mainfile) == 0) {
strcpy (mainfile, infile);
strcpy (datafile, infile);
switch (format) {
case POV10:
case POV20: add_ext (mainfile, "pov", true);
add_ext (datafile, "inc", true); break;
case VIVID: add_ext (mainfile, "v", true);
add_ext (datafile, "vo", true); break;
case RAW: add_ext (datafile, "raw", true); break;
}
}
switch (format) {
case POV10:
case POV20: add_ext (mainfile, "pov", false);
add_ext (datafile, "inc", false); break;
case VIVID: add_ext (mainfile, "v", false);
add_ext (datafile, "vo", false); break;
case RAW: add_ext (datafile, "raw", false); break;
}
if (format != NONE) {
if (format != RAW) {
out.open (mainfile, ios::out);
if (!out) abortmsg ("Error opening output file.", 1);
}
data.open (datafile, ios::out);
if (!data) abortmsg ("Error opening output file.", 1);
}
if (depth < 1)
abortmsg ("Recurse depth (-r) must be >= 1", 1);
cout << "Generating fractal\n";
if (display) {
set_view();
if (init_display() < 0)
abortmsg ("Unable to initialize graphics display.", 1);
}
write_options();
switch (format) {
case POV10: out << "#declare FracTexture = texture {\n";
out << " ambient 0.1 diffuse 0.8\n";
out << " color red 1.0 green 1.0 blue 1.0\n";
out << "}\n\n";
out << "#include \"" << datafile << "\"\n\n";
data << "composite {\n";
break;
case POV20: out << "#declare FracTexture = texture {\n";
out << " finish { ambient 0.1 diffuse 0.8 }\n";
out << " pigment { color red 1.0 green 1.0 blue 1.0 }\n";
out << "}\n\n";
out << "#include \"" << datafile << "\"\n\n";
data << "union {\n";
break;
case VIVID: out << "surface {\n";
out << " ambient 0.1 0.1 0.1\n";
out << " diffuse 0.8 0.8 0.8\n";
out << " shine 70, 1.0 1.0 1.0\n";
out << "}\n\n";
out << "#include " << datafile << "\n\n";
break;
}
if (format == POV20)
Vector::set_delim (',');
Vector a, b, c, vmin, vmax;
int fix_ab, fix_bc, fix_ca, tri_read = 0;
Vector gmin = Vector (+MAXFLOAT, +MAXFLOAT, +MAXFLOAT);
Vector gmax = Vector (-MAXFLOAT, -MAXFLOAT, -MAXFLOAT);
while (read_triangle (a, b, c, fix_ab, fix_bc, fix_ca)) {
tri_fractal (a, b, c, fix_ab, fix_bc, fix_ca, depth, vmin, vmax);
gmin = min (gmin, vmin);
gmax = max (gmax, vmax);
tri_read++;
}
switch (format) {
case POV10:
data << "\tbounded_by { box { <" << gmin << "> <" << gmax << "> } }\n";
data << "}\n\n";
break;
case POV20:
data << "\n\ttexture { FracTexture }\n";
data << "}\n\n";
break;
}
if (format != RAW && format != NONE) {
write_camera (out, viewpoint, lookat);
write_light (out, viewpoint);
}
in.close();
if (format != NONE) {
data.close();
if (format != RAW)
out.close();
}
cerr << "\n" << tri_count << " triangles generated.\n";
if (display) {
cerr << "Press return.\n";
getchar();
close_display();
}
return 0;
}
void usage()
{
cerr << "Usage: frgen infile[.fr] [outfile] [options]\n";
cerr << "Options: -sx, -sy, -sz Scale noise\n";
cerr << " -bx, -by, -bz Bias noise\n";
cerr << " -fx, -fy, -fz Set noise falloff\n";
cerr << " -vx, -vy, -vz Set viewpoint position\n";
cerr << " -lx, -ly, -lz Set lookat position\n";
cerr << " -ennn Use nnn as seed for random number generator\n";
cerr << " -rnnn Maximum recursion depth of nnn\n";
cerr << " -d Display generated fractal on screen\n";
cerr << " -op Output to POV-Ray 2.0 format (default)\n";
cerr << " -op1 Output to POV-Ray 1.0 format\n";
cerr << " -ov Output to Vivid 2.0 format\n";
cerr << " -or Output to RAW triangle format\n";
cerr << " -on Null output (for quick preview).\n";
cerr << " -x Exchange Y and Z coords on output.\n";
}
void abortmsg (char *msg, int exit_code)
{
if (display)
close_display();
cerr << msg << "\n";
exit (exit_code);
}
void add_ext (char *fname, char *ext, bool force)
{
int i;
for (i = 0; i < strlen(fname); i++)
if (fname[i] == '.') break;
if (fname[i] == '\0' || force) {
fname[i] = '.';
strcpy (&fname[i+1], ext);
}
}
void swap (float &a, float &b)
{
float temp = a;
a = b;
b = temp;
}
void process_args (int argc, char* argv[])
{
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-' || argv[i][0] == '/')
process_option (&argv[i][1]);
}
}
void file_args()
{
char line[100] = "";
char last_ch = ' ';
do {
in.getline (line, 100);
input_line++;
} while (!in.eof() && line[0] == ';');
if (in.eof())
return;
for (int i = 0; i < strlen (line); i++) {
if ((line[i] == '-' || line[i] == '/') && isspace(last_ch))
process_option (&line[i+1]);
last_ch = line[i];
}
}
void process_option (char *s)
{
switch (upcase(s[0])) {
case 'V':
switch (upcase(s[1])) {
case 'X': viewpoint.x = atof(&s[2]); break;
case 'Y': viewpoint.y = atof(&s[2]); break;
case 'Z': viewpoint.z = atof(&s[2]); break;
default : cerr << "Invalid option -v" << s[1] << "\n";
}
break;
case 'L':
switch (upcase(s[1])) {
case 'X': lookat.x = atof(&s[2]); break;
case 'Y': lookat.y = atof(&s[2]); break;
case 'Z': lookat.z = atof(&s[2]); break;
default : cerr << "Invalid option -l" << s[1] << "\n";
}
break;
case 'S':
switch (upcase(s[1])) {
case 'X': nscale.x = atof(&s[2]); break;
case 'Y': nscale.y = atof(&s[2]); break;
case 'Z': nscale.z = atof(&s[2]); break;
default : float value = atof(&s[1]);
nscale = Vector (value, value, value);
}
break;
case 'B':
switch (upcase(s[1])) {
case 'X': nbias.x = atof(&s[2]); break;
case 'Y': nbias.y = atof(&s[2]); break;
case 'Z': nbias.z = atof(&s[2]); break;
default : float value = atof(&s[1]);
nbias = Vector (value, value, value);
}
break;
case 'F':
switch (upcase(s[1])) {
case 'X': nfalloff.x = atof(&s[2]); break;
case 'Y': nfalloff.y = atof(&s[2]); break;
case 'Z': nfalloff.z = atof(&s[2]); break;
default : float value = atof(&s[1]);
nfalloff = Vector (value, value, value);
}
break;
case 'E': seed = atoi(&s[1]);
break;
case 'R': depth = atoi(&s[1]);
break;
case 'D': display = true;
break;
case 'X': swap_yz = true;
break;
case 'O' :
switch (upcase(s[1])) {
case 'P': format = (s[2] == '2') ? POV20 : POV10; break;
case 'V': format = VIVID; break;
case 'R': format = RAW; break;
case 'N': format = NONE; break;
default : cerr <<"Invalid option -o" << s[1] << ", ignored\n";
}
break;
default : cerr << "Invalid option -" << s[1] << ", ignored\n";
}
}
void write_options()
{
if (format == POV10 || format == POV20 || format == VIVID) {
data << "/*\n";
data << " Generated with FRGEN " << ver << " from file " << infile << "\n";
data << " Options in effect:";
data << " -sx" << nscale.x << " -sy" << nscale.y << " -sz" << nscale.z;
data << " -bx" << nbias.x << " -by" << nbias.y << " -bz" << nbias.z << "\n";
data << " -fx" << nfalloff.x << " -fy" << nfalloff.y << " -fz" << nfalloff.z;
data << " -r" << depth << " -e" << seed << "\n";
data << "*/\n\n";
}
else if (format == NONE)
cout << "Seed: -e" << seed << "\n";
}
bool read_triangle (Vector &a, Vector &b, Vector &c,
int &fix_ab, int &fix_bc, int &fix_ca)
{
char line[256] = "";
char msg[40];
int n;
do {
in.getline (line, 256);
input_line++;
} while (!in.eof() && line[0] == ';');
if (in.eof())
return false;
a = Vector (0.0, 0.0, 0.0);
b = Vector (0.0, 0.0, 0.0);
c = Vector (0.0, 0.0, 0.0);
fix_ab = 0;
fix_bc = 0;
fix_ca = 0;
n = sscanf (line, "%f %f %f %f %f %f %f %f %f %d %d %d",
&a.x, &a.y, &a.z, &b.x, &b.y, &b.z, &c.x, &c.y, &c.z,
&fix_ab, &fix_bc, &fix_ca);
if (n == EOF)
return false;
if (!(n == 9 || n == 12)) {
sprintf (msg, "Error in input file, line %d", input_line);
abortmsg (msg, 1);
}
return true;
}
Vector noise_vector (Vector &v, int level)
{
// Generate a random vector that is a function of the
// vector v's position
Vector noise, scale;
scale.x = nscale.x * exp (level * log (nfalloff.x));
scale.y = nscale.y * exp (level * log (nfalloff.y));
scale.z = nscale.z * exp (level * log (nfalloff.z));
// seed the rand # generator with a mish-mash of the x, y, and z coords
srand (seed ^ (long)(-23465*v.x) ^ (long)(17365*v.y) ^ (long)(5364*v.z));
noise.x = scale.x * (2.0*rand()/RAND_MAX - 1.0 + nbias.x);
noise.y = scale.y * (2.0*rand()/RAND_MAX - 1.0 + nbias.y);
noise.z = scale.z * (2.0*rand()/RAND_MAX - 1.0 + nbias.z);
return noise;
}
void tri_fractal (Vector &a, Vector &b, Vector &c,
int fix_ab, int fix_bc, int fix_ca, int level,
Vector &vmin, Vector &vmax)
{
Vector ab, bc, ca;
float ab_len, bc_len, ca_len;
vmin = Vector (+MAXFLOAT, +MAXFLOAT, +MAXFLOAT);
vmax = Vector (-MAXFLOAT, -MAXFLOAT, -MAXFLOAT);
if (level == 0) {
check_abort();
if (swap_yz) {
swap (a.y, a.z);
swap (b.y, b.z);
swap (c.y, c.z);
}
switch (format) {
case POV10:
data << "\t\ttriangle { <" << a << "> <" << b << "> <" << c << "> }\n";
break;
case POV20:
data << "\ttriangle { <" << a << ">, <" << b << ">, <" << c << "> }\n";
break;
case VIVID:
data << "polygon {\n";
data << "\tpoints 3\n";
data << "\tvertex " << a << "\n";
data << "\tvertex " << b << "\n";
data << "\tvertex " << c << "\n";
data << "}\n\n";
break;
case RAW:
data << a << " " << b << " " << c << "\n";
break;
}
if (swap_yz) {
swap (a.y, a.z);
swap (b.y, b.z);
swap (c.y, c.z);
}
tri_count++;
if (display)
plot_tri (a, b, c);
vmin = min (vmin, a);
vmin = min (vmin, b);
vmin = min (vmin, c);
vmax = max (vmax, a);
vmax = max (vmax, b);
vmax = max (vmax, c);
}
else {
if (format == POV10) {
if (level == 1) {
data << "\tobject {\n";
data << "\t\tunion {\n";
}
else if (level > 1)
data << "composite {\n";
}
// Find the midpoints of the three line segments
ab = (a + b)/2.0;
bc = (b + c)/2.0;
ca = (c + a)/2.0;
ab_len = mag(a - b);
bc_len = mag(b - c);
ca_len = mag(c - a);
// Compute the normal to the triangle
Vector norm = (b - a) * (c - a);
norm = norm/mag(norm);
// Create some noise proportional to the edge lengths
Vector noise_ab = ab_len * noise_vector (ab, depth - level);
Vector noise_bc = bc_len * noise_vector (bc, depth - level);
Vector noise_ca = ca_len * noise_vector (ca, depth - level);
// Don't let any 'fixed' points move out of the plane
// of the triangle (remove the noise's normal component)
if (fix_ab) noise_ab = noise_ab * norm;
if (fix_bc) noise_bc = noise_bc * norm;
if (fix_ca) noise_ca = noise_ca * norm;
// Perturb the three vectors
ab = ab + noise_ab;
bc = bc + noise_bc;
ca = ca + noise_ca;
Vector new_min[4], new_max[4];
tri_fractal (a, ab, ca, fix_ab, 0, fix_ca, level-1, new_min[0], new_max[0]);
tri_fractal (b, bc, ab, fix_bc, 0, fix_ab, level-1, new_min[1], new_max[1]);
tri_fractal (c, ca, bc, fix_ca, 0, fix_bc, level-1, new_min[2], new_max[2]);
tri_fractal (ab, bc, ca, 0, 0, 0, level-1, new_min[3], new_max[3]);
for (int i = 0; i < 4; i++) {
vmin = min (vmin, new_min[i]);
vmax = max (vmax, new_max[i]);
}
if (format == POV10) {
if (level == 1) {
data << "\t\t}\n\n";
data << "\t\ttexture { FracTexture }\n\n";
data << "\t\tbounded_by { box { <" << vmin << "> <" << vmax << "> } }\n";
data << "\t}\n\n";
}
else if (level > 1) {
data << "\tbounded_by { box { <" << vmin << "> <" << vmax << "> } }\n";
data << "}\n";
}
}
}
}
void write_light (fstream &f, Vector pos)
{
switch (format) {
case POV10:
f << "object {\n";
f << " light_source { <" << pos << "> color red 1 green 1 blue 1 }\n";
f << "}\n\n";
break;
case POV20:
f << "light_source { <" << pos << "> color rgb <1,1,1> }\n\n";
break;
case VIVID:
f << "light {\n";
f << " type point\n";
f << " position " << pos << "\n";
f << " color 1.0 1.0 1.0\n";
f << "}\n\n";
break;
}
}
void write_camera (fstream &f, Vector pos, Vector target)
{
switch (format) {
case POV10:
f << "camera {\n";
f << " location <" << pos << ">\n";
f << " look_at <" << target << ">\n";
f << "}\n\n";
break;
case POV20:
f << "camera {\n";
f << " location <" << pos << ">\n";
f << " look_at <" << target << ">\n";
f << "}\n\n";
break;
case VIVID:
f << "studio {\n";
f << " from " << pos << "\n";
f << " at " << target << "\n";
f << " up 0 0 1\n";
f << " angle 60.0\n";
f << " aspect 4/3\n";
f << " resolution 320 200\n";
f << " antialias none\n";
f << "}\n\n";
break;
}
}
void set_view()
{
Vector sky;
sky = Vector (0.0, 1.0, 0.0);
// Calculate a set of base vectors for the new viewpoint
nz = lookat - viewpoint;
nz = nz/mag(nz);
ny = sky - nz*(nz.y/(nz % nz));
ny = ny/mag(ny);
nx = 1.333 * nz * ny;
}
/* Convert character 'c' top upper case */
char upcase (char c)
{
if (c >= 'a' && c <= 'z')
c = c - 'a' + 'A';
return c;
}
void plot_tri (const Vector &a, const Vector &b, const Vector &c)
{
#ifdef __TURBOC__
Vector pa, pb, pc, ta, tb, tc;
int ax, ay, bx, by, cx, cy;
ta = a - viewpoint;
tb = b - viewpoint;
tc = c - viewpoint;
pa = Vector (ta % nx, ta % ny, ta % nz);
pb = Vector (tb % nx, tb % ny, tb % nz);
pc = Vector (tc % nx, tc % ny, tc % nz);
ax = int((0.5*pa.x/pa.z + 0.5)*getmaxx());
bx = int((0.5*pb.x/pb.z + 0.5)*getmaxx());
cx = int((0.5*pc.x/pc.z + 0.5)*getmaxx());
ay = int((0.5 - pa.y/pa.z)*getmaxy());
by = int((0.5 - pb.y/pb.z)*getmaxy());
cy = int((0.5 - pc.y/pc.z)*getmaxy());
setcolor (LIGHTBLUE);
moveto (ax, ay);
lineto (bx, by);
lineto (cx, cy);
lineto (ax, ay);
#endif
}
int init_display()
{
#ifdef __TURBOC__
int gdriver = VGA, gmode = VGAHI, errorcode;
registerbgidriver (EGAVGA_driver);
initgraph (&gdriver, &gmode, "");
errorcode = graphresult();
if (errorcode != grOk) {
cerr << "ERROR:" << grapherrormsg (errorcode) << "\n\n";
return -1;
}
#endif
return 0;
}
void close_display()
{
#ifdef __TURBOC__
restorecrtmode();
#endif
}
void check_abort()
{
#ifdef __TURBOC__
static int cnt = 0;
++cnt;
if (cnt >= 10) {
cnt = 0;
if (kbhit())
abortmsg ("Aborted!", 1);
}
#endif
}